﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using Hoo.Windows.Shell.Util;
using System.Collections.Generic;


namespace Hoo.Windows.Shell {   
    /// <summary>
    /// Encapsulation class of MENUITEMINFO struct. <p/>
    /// In most cases, you should inherit this class to create new menu.
    /// However, if you do not want write any execution code to the menu. 
    /// In other word, you won't override <code>Execute</code> method. you can directly register a <code>MenuItem</code> object.
    /// </summary>
    public class MenuItem : IDisposable {
        protected MENUITEMINFO _menuItemInfo;

        /**
         * Readonly, gets a MENUITEMINFO instance for Win32 API invoking.
         **/
        public MENUITEMINFO MenuItemInfo {
            get { return _menuItemInfo; }
        }

        public MenuItem() {
            _menuItemInfo = new MENUITEMINFO();
            _menuItemInfo.cbSize = (uint)Marshal.SizeOf(typeof(MENUITEMINFO)); //Generally it is 48;            
        }

        ~MenuItem() {
            Logger.Debug("Begin de-construct MenuItem!");
            Dispose(true);
        }

        public void Dispose() {

            Logger.Debug("Begin dispose MenuItem!");
            Dispose(true);

            GC.SuppressFinalize(this);

        }

        protected void Dispose(bool disposing) {

        }
        
        public uint Position { get; set; }

        public int ID {
            get {
                return _menuItemInfo.wID;
            }
            set {                
                _menuItemInfo.fMask |= (uint)MIIM.ID;
                _menuItemInfo.wID = value;
            }
        }

        public string Title {
            get { return _menuItemInfo.dwTypeData; }
            protected set {                
                _menuItemInfo.fMask |= (uint)MIIM.STRING;
                _menuItemInfo.dwTypeData = value;
            }
        }


        protected Icon Icon {
            set {                
                _menuItemInfo.fMask |= (uint)MIIM.BITMAP;
                _menuItemInfo.hbmpItem = GetHbitmap(value.ToBitmap());
            }

        }

        protected Bitmap IconBitmap {
            set {                
                _menuItemInfo.fMask |= (uint)MIIM.BITMAP;
                _menuItemInfo.hbmpItem = GetHbitmap(value);
            }
        }

        public MFS State {
            get { return (MFS)_menuItemInfo.fState; }
            set {
                _menuItemInfo.fMask |= (uint)MIIM.STATE;
                _menuItemInfo.fState = (uint)value;
            }
        }

        protected Icon CheckedIcon {
            set {
                _menuItemInfo.fMask |= (uint)MIIM.CHECKMARKS;
                _menuItemInfo.hbmpChecked = GetHbitmap(value.ToBitmap());
            }

        }

        protected Icon UncheckedIcon {
            set {
                _menuItemInfo.fMask |= (uint)MIIM.CHECKMARKS;
                _menuItemInfo.hbmpUnchecked = GetHbitmap(value.ToBitmap());
            }

        }

        protected Bitmap CheckedIconBitmap {
            set {
                _menuItemInfo.fMask |= (uint)MIIM.CHECKMARKS;
                _menuItemInfo.hbmpChecked = GetHbitmap(value);
            }

        }

        protected Bitmap UncheckedIconBitmap {
            set {
                _menuItemInfo.fMask |= (uint)MIIM.CHECKMARKS;
                _menuItemInfo.hbmpUnchecked = GetHbitmap(value);
            }

        }

        public int SubMenuHandler {
            get {
                return _menuItemInfo.hSubMenu;
            }
            protected set {
                _menuItemInfo.fMask |= (uint)MIIM.SUBMENU;
                _menuItemInfo.hSubMenu = value;
            }
        }

        public MFT Type {
            get { return (MFT)_menuItemInfo.fType; }
            protected set {
                _menuItemInfo.fMask |= (uint)MIIM.FTYPE;
                _menuItemInfo.fType = (uint)value;
            }
        }

        public void Register(uint handler, uint position) {
            try {                
                if (handler <= 0) {
                    throw new Exception("Fail to insert menu " + Title + ". hMenu is missing!");
                }

                Win32Helpers.SetLastError(0);

                Win32Helpers.InsertMenuItem(handler, position, true, ref _menuItemInfo);
                int error = Marshal.GetLastWin32Error();
                if (error != 0) {
                    Exception e1 = new Win32Exception(error);
                    String msg = String.Format("Fail to insert menu {0}, Error code {1}.", Title, error);
                    throw new Exception(msg, e1);                    
                }

                //int menuId = Win32Helpers.GetMenuItemID(hMenu, iMenu + 2);
                //Logger.Debug(String.Format("Menu position: {0}, WID: {1}, First ID: {2}, ID: {3}", (uint)iMenu + 2, id, idCmdFirst, menuId));
            } catch (Exception e) {
                Logger.Error(e);
            }
        }

        /// <summary>
        /// Registers the specified handler.
        /// </summary>
        /// <param name="handler">The handler.</param>
        private void Register(uint handler) {
            try {
                Win32Helpers.SetLastError(0);
                if ((_menuItemInfo.fMask & (uint)MIIM.ID) == 0) {
                    String msg = "Fail to insert menu " + Title + ". It's ID missed!";
                    throw new Exception(msg);                    
                }

                //TODO verify this API
                Win32Helpers.InsertMenuItem(handler, (uint)_menuItemInfo.wID, false, ref _menuItemInfo);
                int error = Marshal.GetLastWin32Error();
                if (error != 0) {
                    Exception e = new Win32Exception(error);
                    String msg = String.Format("Fail to insert menu {0}, Error code {1}.", Title, error);
                    throw new Exception(msg, e);
                    
                }
            } catch (Exception e) {
                Logger.Error(e);
            }
        }

        /// <summary>
        /// Gets the handle of specified bitmap.
        /// </summary>
        /// <param name="bmp"></param>
        /// <returns>Integer, handler of bitmap</returns>
        /// <remarks>
        /// In order to show bitmap as context menu's icon, Please make sure:
        /// <ol>
        ///     <li>Size is 16x16 for general icon</li>
        ///     <li>Size is 12x12 for checked / unchecked icon</li>
        ///     <li>Backcolor is transparent or white</li>
        /// </ol>
        /// In order to make icon more clear and beautiful, 
        /// This function will not resize or adjust the bitmap.
        /// </remarks>
        private int GetHbitmap(Bitmap bmp) {
            Bitmap iconBmp = bmp; 

            int result = iconBmp.GetHbitmap().ToInt32();
            Logger.Debug("Get icon BMP: " + iconBmp.Size + ", hbmpItem: " + result);
            return result;
        }

        public virtual void Execute(string currentDirectory, IList<String> selectedFiles) {
            //To be implemented in extened classes.            
        }

        /// <summary>
        /// Insert a line seperator to windows context menu.
        /// </summary>
        /// <example>
        /// MenuItem.NewMenuSeperator().Register(hMenu, (uint)iMenu);
        /// </example>
        /// <returns></returns>
        public static MenuItem NewMenuSeperator() {
            MenuItem seperator = new MenuItem();
            seperator.Type = MFT.SEPARATOR;

            return seperator;
        }

        //public sealed class MenuSeperator : MenuItem {
        //    public MenuSeperator()
        //        : base() {
        //        Type = MFT.SEPARATOR;
        //    }
        //}

    }

    
}
